#!/usr/bin/env python3 
# -*- coding: utf-8 -*-
import io
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')

from collections import Iterator
from functools import reduce
#偏函数调用
from functools import partial
#枚举
from enum import Enum, unique

##
##
##类创建
class Person(object):
	def __init__(self,name,sex,age):
		self.__name = name
		self.__sex = sex
		self.age = age

	def get_name(self):
		return self.__name
	def get_sex(self):
		return self.__sex

	def set_name(self,name):
		self.__name = name
	def set_sex(self,sex):
		self.__sex = sex

	def run(self):
		print("I'm a Person")

p = Person("绑定值",1,20)

print(p.get_name(),p.get_sex())	

#在类里面添加方法
class Student(Person):
    def __init__(self, name, score):
    	#私有变量
        self.__name = name
        self.__score = score

    def get_grade(self):
        if self.__score >= 90:
            return 'A'
        elif self.__score >= 60:
            return 'B'
        else:
            return 'C'	

    def get_score(self):
    	return self.__score

    def get_name(self):
    	return self.__name

    def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')

    def set_name(self,name):
    		self.__name = name


lisa = Student('Lisa', 99)
bart = Student('Bart', 59)
#print(lisa._Student__name)#__将变量转为私有变量，实际上不限制_Student__name访问，通常用set、get方法进行设置获取
#print(bart._Student__name)
lisa.set_sex("女")
print(lisa.get_name(),lisa.get_grade(),lisa.run(),lisa.get_sex())
print("-----------------")
print(bart.get_name(),bart.get_grade(),bart.run())

#判断类型 type()

print(type(123))

print(type(lisa))

#判断值是否是该类型
print(isinstance(p, Student))
print(isinstance(lisa, Person))
print(isinstance(p, Person))
print(isinstance(123, int))
#判断对象是否存在该属性
class MyObject(object):
     def __init__(self):
         self.x = 9
     def power(self):
         return self.x * self.x
obj = MyObject()
print(hasattr(obj,'sex'))#不存在属性,所以false
print(hasattr(obj,'x'))
setattr(obj, 'y', 19)#设置属性'y'
hasattr(obj, 'y') # 有属性'y'吗？true
#getattr(obj, 'z') # 获取属性'z',不存在会出现异常：
#Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
#AttributeError: 'MyObject' object has no attribute 'z'

#长度及小写
len("abc")
'ABC'.lower()

#装饰器（注解）
class Student1(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
s = Student1()
s.score = 60 # 实际转化为s.set_score(60)
print(s.score) #实际转化为s.get_score()

#birth是可读写属性，而age就是一个只读属性，因为age可以根据birth和当前时间计算出来。
class Student2(object):

    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        self._birth = value

    @property
    def age(self):
        return 2015 - self._birth

#@property广泛应用在类的定义中，可以让调用者写出简短的代码，同时保证对参数进行必要的检查，这样，程序运行时就减少了出错的可能性。

#直接显示变量调用的不是__str__()，而是__repr__()，两者的区别是__str__()返回用户看到的字符串，
#而__repr__()返回程序开发者看到的字符串，也就是说，__repr__()是为调试服务的。

#再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的，所以，有个偷懒的写法：

class Student3(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

s = Student3('Michael')
print(s)

#如果一个类想被用于for ... in循环，类似list或tuple那样，就必须实现一个__iter__()方法，该方法返回一个迭代对象，
#然后，Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值，直到遇到StopIteration错误时退出循环。

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a，b

    def __iter__(self):
        return self # 实例本身就是迭代对象，故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
for n in Fib():
    print(n)

#Fib实例虽然能作用于for循环，看起来和list有点像，但是，把它当成list来使用还是不行，比如，取第5个元素
#Fib()[5]
#要表现得像list那样按照下标取出元素，需要实现__getitem__()方法：

class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

f = Fib()
print(f[5])
#list切片 list(range(100))[5:10]，对于Fib却报错。原因是__getitem__()传入的参数可能是一个int，也可能是一个切片对象slice，
#所以要做判断：
class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
f = Fib()
print(f[0:5])
print(f[:10])

#枚举
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)#value属性则是自动赋给成员的int常量，默认从1开始计数。


#@unique装饰器可以帮助我们检查保证没有重复值。
@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
for name, member in Weekday.__members__.items():
	print(name, '=>', member, ',', member.value)

w = Weekday(0)
print(w)